/**
 * @file sprites.h
 * Sprite handling.
 *
 * Description of sprites.h
 *
 * @addtogroup lnsprites Sprite handling
 * @{
 *
 * @todo Priority functions.
 */

//modified for no libnds collision by thesean sept. 2008
//also minor bug fixes under liran's direction

#ifndef __SPRITES_H__
#define __SPRITES_H__

#include <nds/arm9/trig_lut.h>
#include <nds/arm9/video.h>

#include "ellen/video.h"
//SES - excluded, will not use, save some code space!
//#include "gif.h"
		
// ----------------- Defines	-----------------
		
/**
 * Number of sprites available per screen.
 */
#define LN_SPR_COUNT			(128)
		
/**
 * Number of rotation matrices available per screen.
 */
#define LN_MTX_COUNT			(LN_SPR_COUNT / 4)
		
/**
 * Sprite tile boundry.
 */
#define LN_SPR_TILEBOUNDRY		((DISPLAY_CR >> 20) & 0x03)		
		
/**
 * Number of tiles available.
 */
#define SPR_TILES_COUNT			(1024 << LN_SPR_TILEBOUNDRY)
		
/**
 * Number of tiles available for a color mode.
 */
#define SPR_TILES_COLMODE(c)	(SPR_TILES_COUNT >> (c))
		
/**
 * Number of tiles available for 16col color mode.
 */
#define SPR_TILES_16			(SPR_TILES_COLMODE(COLMODE_16))
		
/**
 * Number of tiles available for 256col color mode.
 */
#define SPR_TILES_256			(SPR_TILES_COLMODE(COLMODE_256))
		
/**
 * A macro to calculate the address of a tile in memory
 *
 * @param scr Screen
 * @param tidx Tile Index
 */
#define LN_SPR_TILEADDR(scr, tidx) \
	((SPRITE_GFX + ((scr) * (SPRITE_GFX_SUB - SPRITE_GFX)))			/* Base address. */ \
	+ ((((tidx) << LN_SPR_TILEBOUNDRY) * SIZEOF_TILE_16) >> 1))		/* Tile offset. */
		
// ----------------- Types		-----------------
		
/**
 * Object display mode.
 */
typedef enum
{
	lnOBJMODE_NORMAL,		/**< No special mode is on - Normal sprite state. */
	lnOBJMODE_BLENDED,	/**< Color blending is on - Sprite can use HW blending features. */
	lnOBJMODE_WINDOWED,	/**< Sprite can be seen only inside the sprite window. */
	lnOBJMODE_BITMAP,		/**< Sprite is not using tiles - per pixel image data. */
		
} lntObjMode;
		
/**
 * Object shape mode.
 */
typedef enum
{
	lnOBJSHAPE_SQUARE,	/**< Sprite shape is NxN (Height == Width). */
	lnOBJSHAPE_WIDE,		/**< Sprite shape is NxM with N > M (Height < Width). */
	lnOBJSHAPE_TALL,		/**< Sprite shape is NxM with N < M (Height > Width). */
	lnOBJSHAPE_FORBIDDEN,	/**< Sprite shape is undefined. */
		
} lntObjShape;
		
/**
 * Object shape size.
 */
typedef enum
{
	lnOBJSIZE_8,		/**< Major sprite size is 8px. */
	lnOBJSIZE_16,		/**< Major sprite size is 16px. */
	lnOBJSIZE_32,		/**< Major sprite size is 32px. */
	lnOBJSIZE_64,		/**< Major sprite size is 64px. */
		
} lntObjSize;
		
/**
 * A Hardware sprite. An exact representation of a single sprite in OAM.
 *
 * @note A rotatable sprite (isRotoscale set) is NOT hide-able (isHidden) or flip-able (hFlip and vFlip)
 */
typedef union lnSprite {
	struct {
			
		struct {
			u16 posY						:8;	/**< Sprite Y position. */
			union {
				struct {
					u8 						:1;
					bool isHidden 			:1;	/**< Sprite is hidden (isRotoscale cleared). */
					u8						:6;
				};
				struct {
					bool isRotoscale		:1;	/**< Sprite uses affine parameters if set. */
					bool rsDouble			:1;	/**< Sprite bounds are doubled (isRotoscale set). */
					lntObjMode objMode		:2;	/**< Sprite object mode. */
					bool isMosic			:1;	/**< Enables mosaic effect if set. */
					lntColMode colMode		:1;	/**< Sprite color mode. */
					lntObjShape objShape		:2;	/**< Sprite shape. */
				};
			};
		};
			
		union {
			struct {
				u16 posX					:9;	/**< Sprite X position. */
				u8 							:7;
			};
			struct {
				u8							:8;
				union {
					struct {
						u8					:4;
						bool hFlip			:1; /**< Flip sprite horizontally (isRotoscale cleared). */
						bool vFlip			:1; /**< Flip sprite vertically (isRotoscale cleared).*/
						u8					:2;
					};
					struct {
						u8					:1;
						u8 rsMatrixIdx		:5; /**< Affine parameter number to use (isRotoscale set). */
						lntObjSize objSize	:2; /**< Sprite size. */
					};
				};
			};
		};
			
		struct {
			u16 tileIdx						:10;/**< Upper-left tile index. */
			lntPriority objPriority			:2;	/**< Sprite priority. */
			u8 objPal						:4;	/**< Sprite palette to use in paletted color modes. */
		};
			
		u16 attribute3;							/**< Four of those are used as a sprite rotation matrice */
	};
		
	u16 attribute[4];							/**< Provides compatibility with libnds */
		
} lnSprite;
		
/**
 * Sprite rotation matrix
 */
typedef struct 
{
	u16:16; u16:16; u16:16;
	u16 XDX;					/**< First matrix entry */
		
	u16:16; u16:16; u16:16;
	u16 XDY;					/**< Second matrix entry */
		
	u16:16; u16:16; u16:16;
	u16 YDX;					/**< Third matrix entry */
		
	u16:16; u16:16; u16:16;
	u16 YDY;					/**< Last matrix entry */
		
} lnSprRot;
		
/**
 * Sprite animation behavior
 */
typedef enum
{
	ANIMTYPE_OFF,		/**< Animation is off */
	ANIMTYPE_LOOP,		/**< Animation is looping N times or unlimited */
	ANIMTYPE_SINGLE,	/**< Animation is being played one time, then ends on last frame */
	ANIMTYPE_BOUNCE,	/**< Animation is changing playback directions when finished */
	ANIMTYPE_RANDOM,	/**< Animation is randomly changing frames */
		
} lntAnimType;
		
/**
 * lnSprite extended animation info
 */
typedef struct
{
	int animTick;			/**< Frames to wait untill next frame change */
	//SES removed 
	//int animSize;			/**< Number of animation frames */
	//SES End
	int animFrame;			/**< Current frame on animation */
	int animFrameInc;		/**< Increase rate for animation */
	int animInterval;		/**< Numbers of frames per frame change */
	int animLoopCnt;		/**< Number of loop count (0 for unlimited) */
	int animStart;			/**< First frame of animation */
	int animEnd;			/**< Last frame of animation */
	int animFrameSize;		/**< Size of a frame in bytes */
	lntAnimType animType;		/**< Animation type (See tAnimType) */
		
	const void* animTiles;	/**< Frame animation tile graphics */
	lnSprite* animSprite;	/**< Linked lnSprite union */
		
#ifdef __GIF_H__
	u32* gifFrameInterval;	/**< GIF animation interval vector */
#endif
		
} lnSpriteAnim;
		
/**
 * Sprite final size.
 */
typedef enum
{
	SPRSIZE_8x8,		/**< Sprite size is 8x8 pixels, 1 tile */
	SPRSIZE_16x8,		/**< Sprite size is 16x8 pixels, 2 tiles */
	SPRSIZE_8x16,		/**< Sprite size is 8x16 pixels, 2 tiles */
	SPRSIZE_16x16,		/**< Sprite size is 16x16 pixels, 4 tiles */
	SPRSIZE_32x8,		/**< Sprite size is 32x8 pixels, 4 tiles */
	SPRSIZE_8x32,		/**< Sprite size is 8x32 pixels, 4 tiles */
	SPRSIZE_32x32,		/**< Sprite size is 32x32 pixels, 16 tiles */
	SPRSIZE_32x16,		/**< Sprite size is 32x16 pixels, 8 tiles */
	SPRSIZE_16x32,		/**< Sprite size is 16x32 pixels, 8 tiles */
	SPRSIZE_64x64,		/**< Sprite size is 64x64 pixels, 64 tiles */
	SPRSIZE_64x32,		/**< Sprite size is 64x32 pixels, 32 tiles */
	SPRSIZE_32x64,		/**< Sprite size is 32x64 pixels, 32 tiles */
		
	SPRSIZE_INVALID,	/**< Sprite size is invalid */
		
} lntSprSize;
		
/**
 * Final OAM representation in memory
 *
 * @tip Advanced users might find this union helpful for double buffered OAM
*/
typedef union {
	lnSprite sprBuffer[LN_SCREEN_COUNT][LN_SPR_COUNT];
	lnSprRot sprRotBuf[LN_SCREEN_COUNT][LN_MTX_COUNT];
} lntOAM;
		
// ----------------- Data		-----------------
		
/**
 * Sprites buffer and rotation buffer combined
 *
 * @note Try to avoid using directly unless you are sure about what you are doing
 */
extern lntOAM oamBuffer;	
		
// ----------------- Functions	-----------------
		
	//	---------- Sprite maintaince ----------------
		
/**
 * Enables sprites and clears the sprite buffer.
 *
 * @complexity O(0)
 */
void lnSpritesInit();
		
/**
 * Copies sprite buffer to OAM
 *
 * @complexity O(0)
 */
void lnSpritesUpdate();
		
	//	---------- Sprite managment ----------------
		
/**
 * Returns a sprite by index
 *
 * @param cScreen Screen to retrieve the sprite on
 * @param sprIdx sprite index (0 - 127)
 * @return Pointer to the the sprite in index sprIdx or NULL for error
 * @note This function does not check if the sprite is used or not
 * @complexity O(0)
 */
lnSprite* lnSpriteByIndex(lntScreen cScreen, int sprIdx);
		
/**
 * Scans for an unused sprite
 *
 * @param cScreen Screen to init the sprite on
 * @return Pointer to the new unused inited sprite or NULL for error
 * @complexity O(N)
 */
lnSprite* lnSpriteNew(lntScreen cScreen);
		
/**
 * Deinits sprite
 *
 * @param cSprite Sprite pointer to be destroyed
 * @complexity O(0)
 */
void lnSpriteDestroy(lnSprite* cSprite);
		
/**
 * Returns an inited sprite with selected attributes
 *
 * @param cScreen Screen to init a sprite to
 * @param sprSize Size of the sprite
 * @param colMode Number of colors in the sprite
 * @param sprData Sprite tiles
 * @param sprPalette Sprite palette
 * @return A pointer to a new sprite with selected attributes or NULL for error
 * @note Using this call, will auto-allocate new sprite tiles and palette
 * @complexity O(0)
 */
lnSprite* lnSpriteCreate(lntScreen cScreen, lntSprSize sprSize, lntColMode colMode, const void* sprData, const void* sprPalette);
		
/**
 * Returns a new duplicated sprite
 *
 * @param srcSprite The sprite to duplicate
 * @param copyGfx Should the routine also copy the sprite's graphics (default off)
 * @return A new allocated sprite, filled with info from input sprite
 * @note The output sprite will be located on the same screen
 * @note Sprite animation information will also be copied if copyGfx is set to true
 * @complexity O(0)
 */
lnSprite* lnSpriteDuplicate(lnSprite* srcSprite, bool copyGfx = false);
		
/**
 * Returns a new duplicated sprite
 *
 * @param srcSprite The sprite to duplicate
 * @return A new allocated sprite, filled with info from input sprite
 * @note The output sprite will be located on the opposite screen
 * @note Sprite animation information will also be copied
 * @complexity O(0)
 */
lnSprite* lnSpriteScreenDuplicate(lnSprite* srcSprite);
		
/**
 * Resizes the sprite to a new size
 *
 * @param cSprite Sprite pointer to resize
 * @param sprSize New size to set the sprite to
 * @complexity O(0)
 */
void lnSpriteResize(lnSprite* cSprite, lntSprSize sprSize);
		
/**
 * Enable sprite rotation
 *
 * @param cSprite Sprite pointer to activate rotation to
 * @param rsDouble if set, sprite's virtual size will be doubled unclipped rotation (default off)
 * @param autoAlloc if true, will allocate new rotation matrix (default on)
 * @note This function will also allocate a free rotation matrix
 * @complexity O(0)
 */
void lnSpriteEnableRotation(lnSprite* cSprite, bool rsDouble = false, bool autoAlloc = true);
		
/**
 * Rotates a sprite 
 *
 * @param cSprite Sprite pointer to rotate
 * @param iAngle Rotation angle 0-511 (corresponding to 0-360)
 * @complexity O(0)
 */
void lnSpriteRotate(lnSprite* cSprite, int iAngle);
		
	//	---------- Sprite information ----------------
		
/**
 * Returns on what screen the sprite is located on
 *
 * @param cSprite The sprite to retreive the screen from
 * @return Screen where the sprite is located on or LN_SCREEN_COUNT for error
 * @complexity O(0)
 */
lntScreen lnSpriteScreen(lnSprite* cSprite);
		
/**
 * Returns the sprite index in OAM
 *
 * @param cSprite The sprite to retrieve the info from
 * @return Sprite's index on OAM (0-127) or -1 on error
 * @complexity O(0)
 */
int lnSpriteIndex(lnSprite* cSprite);
		
/**
 * Returns the sprite's size
 *
 * @param cSprite The sprite to retrieve the info from
 * @return tSprSize type that represends the sprite size
 * @complexity O(0)
 */
lntSprSize lnSpriteSize(lnSprite* cSprite);
		
/**
 * Returns the sprtie size based on pixel size
 *
 * @param dimX Sprite width
 * @param dimY Sprite height
 * @return tSprSize type that represends the sprite size or SPRSIZE_INVALID for error
 * @complexity O(0)
 */
lntSprSize lnSpritePixelSize(u8 dimX, u8 dimY);
		
/**
 * Returns the width of the sprite in pixels
 *
 * @param cSprite The sprite to retreive the width from
 * @return Width of cSprite in pixels
 * @complexity O(0)
 */
u8 lnSpriteWidth(lnSprite* cSprite);
		
/**
 * Returns the height of the sprite in pixels
 *
 * @param cSprite The sprite to retreive the height from
 * @return Height of cSprite in pixels
 * @complexity O(0)
 */
u8 lnSpriteHeight(lnSprite* cSprite);
		
/**
 * Returns the number of tiles the sprite is using in VRAM
 *
 * @param cSprite The sprite to retreive the info from
 * @return Number of tiles the sprite is using
 * @complexity O(0)
 */
int lnSpriteTileCount(lnSprite* cSprite);
		
/**
 * Checks for collision between two sprites
 *
 * @param cSprite1 First sprite for check
 * @param cSprite2 Second sprite for check
 * @return true if sprites collide, false otherwise
 * @note Sprite order does not metter
 * @complexity O(0)
 */
bool lnSpriteCollision(lnSprite* cSprite1, lnSprite* cSprite2);
		
/**
 * Retrieves the tile address of a sprite
 *
 * @param cSprite Sprite to get the info from 
 * @return Pointer to VRAM where the sprite's tiles are stored
 * @complexity O(0)
 */
u16* const lnSpriteGetTiles(lnSprite* cSprite);
		
/**
 * Retrieves the palette address of a sprite
 *
 * @param cSprite Sprite to get the info from 
 * @return Pointer to Palette memory where the sprite's palette is stored
 * @warning 256col sprites are not readable or writeable because of the design of the DS's HW
 * @complexity O(0)
 */
u16* const lnSpriteGetPalette(lnSprite* cSprite);
		
/**
 * Allocates a place for sprite tiles
 *
 * @param cSprite Sprite to allocate the tiles for
 * @return integer between 0-1023 that represents the start of the allocated tiles or -1 for error
 * @note Allocated tiles are guaranteed to be unused by previous sprites
 * @complexity O(N)
 */
int lnSpriteAllocTiles(lnSprite* cSprite);
		
/**
 * Allocates a place for sprite palette
 *
 * @param cSprite Sprite to allocate the palette for
 * @return Integer between 0-15 that represents the free palette or -1 for error
 * @note Allocated tiles are guaranteed to be unused by previous sprites
 * @complexity O(N)
 */
int lnSpriteAllocPallete(lnSprite* cSprite);		
		
/**
 * Allocates a rotation matrix for a sprite
 *
 * @param cSprite Sprite to allocate the matrix for
 * @return Ineger between 0-31 that represents the free matrix or -1 for error
 * @note Allocated matrix are guaranteed to be unused by other sprites
 * @complexity O(N)
 */
int lnSpriteAllocMatrix(lnSprite* cSprite);
		
/**
 * Initilizes sprite color mode and tiles.
 *
 * @param cSprite The sprite to work on
 * @param sprTiles Sprite tiles
 * @param colMode Color mode 
 * @param autoAlloc Auto allocate tile index (Default ON)
 * @note If colMode is different then the sprite's color mode, the attribute will change.
 * @note When autoAlloc is false, the tile index of use will be the sprite's tileIdx field
 * @warning The function does not check for colliding palettes.
 * @complexity With allocatement - O(N); Without - O(0) 
 */
void lnSpriteSetTiles(lnSprite* cSprite, const void* sprTiles, lntColMode colMode, bool autoAlloc = true);
		
/**
 * Initilizes sprite color mode and palette.
 *
 * @param cSprite The sprite to work on
 * @param sprPalette Sprite palette
 * @param colMode Color mode 
 * @param autoAlloc Auto allocate palette index (Default ON)
 * @note If colMode is different then the sprite's color mode, the attribute will change.
 * @warning The function does not check for colliding palettes when autoAlloc is false
 * @complexity With allocatement - O(N); Without - O(0) 
 */
void lnSpriteSetPalette(lnSprite* cSprite, const void* sprPalette, lntColMode colMode, bool autoAlloc = true);
		
	//	---------- Sprite animation ----------------
		
/**
 * Retreives sprite's animation handler
 *
 * @param cSprite The sprite to retrieve animation from
 * @return Animation handler used by the sprite
 * @complexity O(0) 
 */
lnSpriteAnim* lnSpriteAnimGet(lnSprite* cSprite);
		
/**
 * Set a new sprite animation handler to a custom one
 *
 * @param cSprite The sprite to retrieve animation from
 * @param sprAnim A new sprite animation to be set
 * @warning This is a rather advanced function - The animation handler should be set correctly!
 * @complexity O(0) 
 */
void lnSpriteAnimSet(lnSprite* cSprite, lnSpriteAnim sprAnim);
		
/**
 * Initilizes sprite animation on a given sprite
 *
 * @param cSprite The sprite to enable animation on
 * @param animTiles Animation tiles
 * @param frameCount Number of animation frames 
 * @param animInterval Numbers of frames to wait per frame change (default is 12)
 * @return Animation handler used by the sprite
 * @note Animation created is defaulted to unlimited looping animation
 * @complexity O(0) 
 */
lnSpriteAnim* lnSpriteAnimBind(lnSprite* cSprite, const void* animTiles, int frameCount, int animInterval = 12);
		
/**
 * Handles animation handler per frame
 *
 * @param sprAnim Animation handler to handle
 * @note This function is being used internally, but can be used if you got several anim handlers per sprite (advanced)
 * @complexity O(0) 
 */
void lnSpriteAnimHandle(lnSpriteAnim* sprAnim);
		
/**
 * Sets animation loop count
 *
 * @param cSprite Sprite to set animation loop
 * @param animType Animation type
 * @param loopCycles Number of loop cycles to perform (0 for unlimited)
 * @param incInterval number of frames to skip per frame
 * @complexity O(0) 
 */
void lnSpriteAnimLoop(lnSprite* cSprite, lntAnimType animType, int loopCycles = 0, int incInterval = 1);
		
	// GIF routines
#ifdef __GIF_H__
		
/**
 * Returns an inited sprite with selected attributes loaded from a GIF handler
 *
 * @param cScreen Screen to init a sprite to
 * @param gifFileHandler An inited GIF handler
 * @return A pointer to a new sprite with selected attributes or NULL for error
 * @note Function only available with GIF module
 * @complexity O(0)
 */
lnSprite* lnSpriteFromGIF(tScreen cScreen, pGifHandler gifFileHandler);
		
/**
 * Inits a sprite using GIF loaded data
 *
 * @param cSprite Sprite to append GIF info to
 * @param gifFileHandler An inited GIF handler to retrieve the info from
 * @note Function only available with GIF module
 * @complexity O(0)
 */
void lnSpriteSetGIF(lnSprite* cSprite, pGifHandler gifFileHandler);
		
/**
 * Creates a blank animationhandler from GIF information
 *
 * @return Sprite animation handler loaded with info from GIF handler
 * @param gifFileHandler An inited GIF handler to retrieve the info from
 * @note Function only available with GIF module
 * @complexity O(0)
 */
lnSpriteAnim lnSpriteAnimFromGIF(pGifHandler gifFileHandler);
		
#endif
		
#endif
		
/**
 * @}
 */
